package org.zsk.cfl.authagency.graph;

import com.google.common.collect.Lists;
import com.google.common.graph.Graphs;
import com.google.common.graph.MutableNetwork;
import com.google.common.graph.NetworkBuilder;
import lombok.extern.slf4j.Slf4j;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.zsk.cfl.authagency.AuthAgencyEdge;
import org.zsk.cfl.authagency.AuthAgencyNode;
import org.zsk.cfl.authagency.AuthAgencyProperties;

import java.util.*;

/**
 * 授权代表有向图
 * @author zsk
 * 1/9/21 4:54 PM
 */
@Slf4j
@Component
public class AuthAgencyGraph {

    /**
     * 环授权代办配置
     */
    @Autowired
    private AuthAgencyProperties authAgencyProperties;

    private static MutableNetwork<AuthAgencyNode, AuthAgencyEdge> authAgencyNetwork= NetworkBuilder.directed()
            .allowsSelfLoops(false)
            .allowsParallelEdges(false)
            .build();

    public AuthAgencyGraph(){
        init();
    }

    /**
     * 获取授权代办有向图
     * @return 缓存中的授权代办有向图
     */
    public static MutableNetwork<AuthAgencyNode, AuthAgencyEdge> getAuthAgencyNetwork() {
        //取缓存
        return authAgencyNetwork;
    }

    private void init() {
        //从缓存中获取

        //查库构建

        initForTest();
    }

    private void initForTest(){
        AuthAgencyNode authAgencyNode1=new AuthAgencyNode("0000010");
        AuthAgencyNode authAgencyNode2=new AuthAgencyNode("0000020");
        AuthAgencyNode authAgencyNode3=new AuthAgencyNode("0000030");
        AuthAgencyNode authAgencyNode4=new AuthAgencyNode("0000040");
        AuthAgencyNode authAgencyNode5=new AuthAgencyNode("0000050");
        AuthAgencyNode authAgencyNode6=new AuthAgencyNode("0000060");
        AuthAgencyNode authAgencyNode7=new AuthAgencyNode("0000070");
        AuthAgencyNode authAgencyNode8=new AuthAgencyNode("0000080");
        Calendar calendar=Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.HOUR_OF_DAY,24);

        AuthAgencyEdge authAgencyEdge1to2=new AuthAgencyEdge("0102","00",false,new Date(),calendar.getTime());
        AuthAgencyEdge authAgencyEdge1to3=new AuthAgencyEdge("0103","00",false,new Date(),calendar.getTime());
        AuthAgencyEdge authAgencyEdge2to4=new AuthAgencyEdge("0204","00",false,new Date(),calendar.getTime());
        AuthAgencyEdge authAgencyEdge2to5=new AuthAgencyEdge("0205","00",false,new Date(),calendar.getTime());
        AuthAgencyEdge authAgencyEdge4to7=new AuthAgencyEdge("0407","00",false,new Date(),calendar.getTime());
        authAgencyNetwork.addEdge(authAgencyNode1,authAgencyNode2,authAgencyEdge1to2);
        authAgencyNetwork.addEdge(authAgencyNode1,authAgencyNode3,authAgencyEdge1to3);
        authAgencyNetwork.addEdge(authAgencyNode2,authAgencyNode4,authAgencyEdge2to4);
        authAgencyNetwork.addEdge(authAgencyNode2,authAgencyNode5,authAgencyEdge2to5);
        authAgencyNetwork.addEdge(authAgencyNode4,authAgencyNode7,authAgencyEdge4to7);

        AuthAgencyEdge authAgencyEdge3to6=new AuthAgencyEdge("0306","00",false,new Date(),calendar.getTime());
        authAgencyNetwork.addEdge(authAgencyNode3,authAgencyNode6,authAgencyEdge3to6);

        AuthAgencyEdge authAgencyEdge3to7=new AuthAgencyEdge("0307","00",true,new Date(),calendar.getTime());
        AuthAgencyEdge authAgencyEdge3to8=new AuthAgencyEdge("0308","00",false,new Date(),calendar.getTime());
        AuthAgencyEdge authAgencyEdge8to7=new AuthAgencyEdge("0807","00",true,new Date(),calendar.getTime());
        authAgencyNetwork.addEdge(authAgencyNode3,authAgencyNode7,authAgencyEdge3to7);
        authAgencyNetwork.addEdge(authAgencyNode3,authAgencyNode8,authAgencyEdge3to8);
        authAgencyNetwork.addEdge(authAgencyNode8,authAgencyNode7,authAgencyEdge8to7);

        AuthAgencyEdge authAgencyEdge6to3=new AuthAgencyEdge("0603","00",false,new Date(),calendar.getTime());
        authAgencyNetwork.addEdge(authAgencyNode6,authAgencyNode3,authAgencyEdge6to3);
    }

    /**
     * 授权代办
     * @param authorizerUuid 授权人uuid
     * @param authorizeeUuid 被授权人uuid
     * @param authInfo 授权信息
     * @return 授权成功返回true，否则返回false
     */
    public boolean auth(String authorizerUuid, String authorizeeUuid,AuthAgencyEdge authInfo){
        //校验
        AuthAgencyNode authorizer=new AuthAgencyNode(authorizerUuid);
        AuthAgencyNode authorizee=new AuthAgencyNode(authorizeeUuid);
        //是否已存在授权代办信息
        if (authAgencyNetwork.hasEdgeConnecting(authorizer,authorizee)){
            throw new RuntimeException("授权关系已存在");
        }

        authAgencyNetwork.addEdge(authorizer,authorizee,authInfo);
        //是否闭环授权
        if (!authAgencyProperties.isEnabled()){
            if (!Graphs.hasCycle(authAgencyNetwork)){
                //入口授权代办运行表

                //更新缓存

                return true;
            }
            //闭环授权，删除授权代表信息
            authAgencyNetwork.removeEdge(authInfo);
            return false;
        }else {
            //入口授权代办运行表

            //更新缓存

            return true;
        }
    }

    /**
     * 搜索授权代办链
     * @param authorizerUuid 授权人uuid
     * @param authorizeeUuid 被授权人uuid
     * @return 授权链集合
     */
    public List<MutableNetwork<AuthAgencyNode,AuthAgencyEdge>> searchAuthAgencyLinks(String authorizerUuid, String authorizeeUuid){
        //校验
        AuthAgencyNode authorizer=new AuthAgencyNode(authorizerUuid);
        AuthAgencyNode authorizee=new AuthAgencyNode(authorizeeUuid);
        if (!authAgencyNetwork.nodes().contains(authorizer) || !authAgencyNetwork.nodes().contains(authorizee)){
            log.info("授权人-{} 或 被授权人-{} 无相关授权代办信息",authorizerUuid,authorizeeUuid);
            throw new RuntimeException("授权人或被授权人无相关授权代办信息");
        }

        Set<AuthAgencyEdge> inEdges=authAgencyNetwork.inEdges(authorizee);
        List<MutableNetwork<AuthAgencyNode,AuthAgencyEdge>> authAgencyLinks=new ArrayList<>(inEdges.size());

        for (AuthAgencyEdge inEdge:
                inEdges) {
            MutableNetwork<AuthAgencyNode,AuthAgencyEdge> curAuthAgencyLink=NetworkBuilder.directed().allowsSelfLoops(false).allowsParallelEdges(false).build();
            AuthAgencyNode curSource=authAgencyNetwork.incidentNodes(inEdge).source();
            AuthAgencyNode curTarget=authAgencyNetwork.incidentNodes(inEdge).target();
            curAuthAgencyLink.addEdge(curSource,curTarget,inEdge);
            if (curSource.equals(authorizer)){
                authAgencyLinks.add(curAuthAgencyLink);
            }else if (!inEdge.isOnlySelfTask()){
                collectPrefixAuthAgencyLink(authAgencyLinks,authAgencyNetwork,curAuthAgencyLink,authAgencyNetwork.inEdges(curSource),authorizer);
            }
        }
        //查询授权链
        return authAgencyLinks;
    }

    /**
     * 递归收集授权代办链
     * @param authAgencyLinks 授权代办链集合
     * @param originAuthAgencyNetwork 原始授权代办有向图
     * @param authAgencyLink 当前授权代办链
     * @param inEdges 当前授权代办链首节点入线
     * @param authorizer 授权人
     */
    private void collectPrefixAuthAgencyLink(List<MutableNetwork<AuthAgencyNode,AuthAgencyEdge>>authAgencyLinks,
                                         MutableNetwork<AuthAgencyNode,AuthAgencyEdge> originAuthAgencyNetwork,
                                         MutableNetwork<AuthAgencyNode,AuthAgencyEdge> authAgencyLink,
                                         Set<AuthAgencyEdge> inEdges,
                                         AuthAgencyNode authorizer){
        for (AuthAgencyEdge inEdge:
                inEdges) {
            //当前时间戳
            long currentTimeMillis=System.currentTimeMillis();
            if (inEdge.getAuthStartTime().getTime()>currentTimeMillis){
                log.info("授权代办未生效，生效时间为 {}-{}",inEdge.getAuthStartTime(),inEdge.getAuthEndTime());
                continue;
            }else if (inEdge.getAuthEndTime().getTime()<currentTimeMillis){
                log.info("授权代办已失效，生效时间为 {}-{}",inEdge.getAuthStartTime(),inEdge.getAuthEndTime());
                //从源授权代办有向图中删除该边，等半年后由运维人员将其入库授权代办历史表
                originAuthAgencyNetwork.removeEdge(inEdge);
                //更新缓存
                continue;
            }
            MutableNetwork<AuthAgencyNode,AuthAgencyEdge> curAuthAgencyLink= Graphs.copyOf(authAgencyLink);
            AuthAgencyNode curSource=originAuthAgencyNetwork.incidentNodes(inEdge).source();
            AuthAgencyNode curTarget=originAuthAgencyNetwork.incidentNodes(inEdge).target();
            //出现闭环
            if (curAuthAgencyLink.nodes().contains(curSource)){
                continue;
            }
            curAuthAgencyLink.addEdge(curSource,curTarget,inEdge);
            //找到目标节点结束
            if (curSource.equals(authorizer)){
                authAgencyLinks.add(curAuthAgencyLink);
            }else if (!inEdge.isOnlySelfTask()){
                collectPrefixAuthAgencyLink(authAgencyLinks,originAuthAgencyNetwork,curAuthAgencyLink,originAuthAgencyNetwork.inEdges(curSource),authorizer);
            }
        }

    }

    /**
     * 查询被授权代办的任务
     * @param authorizeeUuid 被授权人uuid
     * @return 被授权代办的任务
     */
    public List<Task> searchAuthTaskByAuthorizee(String authorizeeUuid){
        //查找授权代办有向子图
        MutableNetwork<AuthAgencyNode,AuthAgencyEdge> authAgencySubNetwork=searchAuthAgencySubNetworkByAuthorizee(authorizeeUuid);
        //从授权代办有向子图删除自身，只查授权代办任务
        authAgencySubNetwork.removeNode(new AuthAgencyNode(authorizeeUuid));
        //根据授权代办有向子图查找被授权的代办任务
        //TODO
        return null;
    }

    /**
     * 搜索授权代办有向子图
     * @param authorizeeUuid 被授权人uuid
     * @return 授权代办有向子图
     */
    public MutableNetwork<AuthAgencyNode, AuthAgencyEdge> searchAuthAgencySubNetworkByAuthorizee(String authorizeeUuid) {
        //校验
        AuthAgencyNode authorizee=new AuthAgencyNode(authorizeeUuid);
        if (!authAgencyNetwork.nodes().contains(authorizee)){
            log.info("被授权人-{} 不存在授权信息",authorizeeUuid);
            throw new RuntimeException("被授权人不存在授权信息");
        }

        Set<AuthAgencyEdge> inEdges=authAgencyNetwork.inEdges(authorizee);
        MutableNetwork<AuthAgencyNode,AuthAgencyEdge> authAgencySubNetwork=NetworkBuilder.directed().allowsSelfLoops(false).allowsParallelEdges(false).build();

        for (AuthAgencyEdge inEdge:
                inEdges) {
            AuthAgencyNode curSource=authAgencyNetwork.incidentNodes(inEdge).source();
            AuthAgencyNode curTarget=authAgencyNetwork.incidentNodes(inEdge).target();
            authAgencySubNetwork.addEdge(curSource,curTarget,inEdge);
            if (!inEdge.isOnlySelfTask()){
                collectPrefixAuthAgencySubNetwork(authAgencyNetwork,authAgencySubNetwork,authAgencyNetwork.inEdges(curSource));
            }
        }

        log.debug("授权代办子图：{}",authAgencySubNetwork);

        return authAgencySubNetwork;
    }

    /**
     * 递归收集授权代办有向子图
     * @param originAuthAgencyNetwork 源授权代办有向图
     * @param authAgencySubNetwork 授权代办有向子图
     * @param inEdges 当前授权代办有向子图首节点入线
     */
    private void collectPrefixAuthAgencySubNetwork(MutableNetwork<AuthAgencyNode, AuthAgencyEdge> originAuthAgencyNetwork,
                                                   MutableNetwork<AuthAgencyNode, AuthAgencyEdge> authAgencySubNetwork,
                                                   Set<AuthAgencyEdge> inEdges) {
        for (AuthAgencyEdge inEdge:
                inEdges) {
            //当前时间戳
            long currentTimeMillis=System.currentTimeMillis();
            if (inEdge.getAuthStartTime().getTime()>currentTimeMillis){
                log.info("授权代办未生效，生效时间为 {}-{}",inEdge.getAuthStartTime(),inEdge.getAuthEndTime());
                continue;
            }else if (inEdge.getAuthEndTime().getTime()<currentTimeMillis){
                log.info("授权代办已失效，生效时间为 {}-{}",inEdge.getAuthStartTime(),inEdge.getAuthEndTime());
                //从源授权代办有向图中删除该边，等半年后由运维人员将其入库授权代办历史表
                originAuthAgencyNetwork.removeEdge(inEdge);
                //更新缓存
                continue;
            }
            AuthAgencyNode curSource=originAuthAgencyNetwork.incidentNodes(inEdge).source();
            AuthAgencyNode curTarget=originAuthAgencyNetwork.incidentNodes(inEdge).target();

            //包含该源节点
            if (authAgencySubNetwork.nodes().contains(curSource)){
                authAgencySubNetwork.addEdge(curSource,curTarget,inEdge);
                continue;
            }

            authAgencySubNetwork.addEdge(curSource,curTarget,inEdge);

            //允许传递授权代办且未出现闭环
            if (!inEdge.isOnlySelfTask()){
                collectPrefixAuthAgencySubNetwork(authAgencyNetwork,authAgencySubNetwork,authAgencyNetwork.inEdges(curSource));
            }
        }
    }

    /**
     * 查询已授权代办的任务
     * @param authorizerUuid 被授权人uuid
     * @return 已授权代办的任务
     */
    public List<Task> searchAuthTaskByAuthorizer(String authorizerUuid){
        //校验
        AuthAgencyNode authorizer=new AuthAgencyNode(authorizerUuid);
        if (!authAgencyNetwork.nodes().contains(authorizer)){
            log.info("授权人-{} 不存在授权信息",authorizerUuid);
            throw new RuntimeException("授权人不存在授权信息");
        }
        Set<AuthAgencyEdge> inEdges=authAgencyNetwork.inEdges(authorizer);
        //仅授权代办自身任务
        long count=inEdges.stream()
                .filter(authAgencyEdge -> !authAgencyEdge.isOnlySelfTask())
                .count();
        if (count==0){
            //该授权人无授权他人代办任务
            return Collections.emptyList();
        }else if (count==inEdges.size()){
            //查询自身任务即所有已授权代办的任务
            //TODO
            return null;
        }else {
            //查找授权代办有向子图
            MutableNetwork<AuthAgencyNode,AuthAgencyEdge> authAgencySubNetwork=searchAuthAgencySubNetworkByAuthorizee(authorizerUuid);
            //根据授权有向子图查找已授权的代办任务
            //TODO
        }
        return null;
    }

    /**
     * 办理授权代办任务
     * @param taskId 任务id
     * @param authorizeeUuid 被授权人uuid
     */
    public void completeAuthTask(String taskId, String authorizeeUuid){
        //根据任务id查找办理人
        String authorizerUuid="";
        //校验办理人与被授权人是否存在授权代办关系
        if (CollectionUtils.isEmpty(searchAuthAgencyLinks(authorizerUuid,authorizeeUuid))){
            throw new RuntimeException("该被授权人无权办理该任务");
        }
        //办理任务
        //入库授权任务办理
    }
}
